package article

import (
	"context"
	"gitee.com/DonHz/basic-go/webook/internal/domain"
	"gitee.com/DonHz/basic-go/webook/internal/repository"
	"gitee.com/DonHz/basic-go/webook/internal/repository/cache"
	dao "gitee.com/DonHz/basic-go/webook/internal/repository/dao/article"
	"gitee.com/DonHz/basic-go/webook/pkg/logger"
	"github.com/ecodeclub/ekit/slice"
	"time"
)

type ArticleRepository interface {
	Create(ctx context.Context, article domain.Article) (int64, error)
	Update(ctx context.Context, article domain.Article) error
	// Sync 存储并同步数据
	Sync(ctx context.Context, article domain.Article) (int64, error)
	SyncV1(ctx context.Context, article domain.Article) (int64, error)
	SyncStatus(ctx context.Context, article domain.Article) error
	List(ctx context.Context, art domain.Article, offset int, limit int) ([]domain.Article, error)
	GetById(ctx context.Context, uid int64) (domain.Article, error)
	preCache(ctx context.Context, data []domain.Article)
	GetPublishedById(ctx context.Context, id int64) (domain.Article, error)
}

type CachedArticleRepository struct {
	dao dao.ArticleDAO
	userRepo repository.UserRepository

	reader dao.ReaderDAO
	author dao.AuthorDAO
	cache cache.ArticleCache
	l logger.Logger
}

func NewArticleRepository(dao dao.ArticleDAO, userRepo repository.UserRepository, l logger.Logger,
	) ArticleRepository {
	return &CachedArticleRepository{
		dao: dao,
		userRepo: userRepo,
		l: l,
	}
}

func (a *CachedArticleRepository) Create(ctx context.Context, article domain.Article) (int64, error) {
	// 如果有数据变更，则清空缓存
	defer func() {
		err:=a.cache.DelFirstPage(ctx, article.Author.Id)
		if err!=nil{
			a.l.Error("清空缓存失败", logger.Error(err))
		}
	}()
	return a.dao.Insert(ctx, dao.Article{
		Title:    article.Title,
		Content:  article.Content,
		AuthorId: article.Author.Id,
		Status: article.Status.ToUint8(),
	})
}

func (a *CachedArticleRepository) Update(ctx context.Context, article domain.Article) error {
	// 如果有数据变更，则清空缓存
	defer func() {
		err:=a.cache.DelFirstPage(ctx, article.Author.Id)
		if err!=nil{
			a.l.Error("清空缓存失败", logger.Error(err))
		}
	}()
	return a.dao.UpdateByArticleId(ctx, dao.Article{
		Id:       article.Id,
		Title:    article.Title,
		Content:  article.Content,
		AuthorId: article.Author.Id,
		Status: article.Status.ToUint8(),
	})
}

func (a *CachedArticleRepository) Sync(ctx context.Context, article domain.Article) (int64, error) {
	// 如果有数据变更，则清空缓存
	id, err:=a.dao.Sync(ctx, a.toEntity(article))
	if err== nil{
		// 提前缓存好线上库
		err:=a.cache.DelFirstPage(ctx, article.Author.Id)
		if err!=nil{
			a.l.Error("清空缓存失败", logger.Error(err))
		}
		err = a.cache.SetPub(ctx, article)
		if err!=nil{
			a.l.Error("提前缓存线上库失败", logger.Error(err))
		}
	}
	return id, err
}


func (a *CachedArticleRepository) SyncV1(ctx context.Context, article domain.Article) (int64, error) {
	// 如果有数据变更，则清空缓存
	defer func() {
		err:=a.cache.DelFirstPage(ctx, article.Author.Id)
		if err!=nil{
			a.l.Error("清空缓存失败", logger.Error(err))
		}
	}()
	var (
		id  = article.Id
		err error
	)
	articlen := a.toEntity(article)
	// 先保存到制作库，再保存到线上库
	if id == 0 {
		id, err = a.author.Insert(ctx, articlen)
	} else {
		err = a.author.UpdateByArticleId(ctx, articlen)
	}
	if err != nil {
		return id, err
	}

	// 线上库，可能有（已发表），可能没有（在草稿箱）
	id, err = a.reader.Upsert(ctx, articlen)
	return id, err
}

func (a *CachedArticleRepository) SyncStatus(ctx context.Context, article domain.Article) error{
	return a.dao.SyncStatus(ctx, a.toEntity(article))
}

func (a *CachedArticleRepository) List(ctx context.Context, art domain.Article, offset int, limit int) ([]domain.Article, error){
	if offset==0 && limit<=100 {
		data, err:=a.cache.GetFirstPage(ctx, art.Author.Id)
		if err!=nil {
			return nil, err
		}
		go func() {
			a.preCache(ctx, data)
		}()
		return data[:limit], nil
	}

	res, err:=a.dao.GetByAuthor(ctx, art, offset, limit)
	if err!=nil{
		return nil ,err
	}
	data := slice.Map[dao.Article, domain.Article](res, func(idx int, src dao.Article) domain.Article {
		return a.toDomain(src)
	})
	// 回写缓存
	go func() {
		err=a.cache.SetFirstPage(ctx, art.Author.Id, data)
		if err!=nil{
			a.l.Error("回写缓存失败", logger.Error(err))
		}
		a.preCache(ctx, data)
	}()
	return data, nil
}

func (a *CachedArticleRepository) preCache(ctx context.Context, data []domain.Article) {
	if len(data)>0 && len(data[0].Content)<1024*1024 {
		err:=a.cache.Set(ctx, data[0])
		if err!=nil{
			a.l.Error("提前预加载缓存失败", logger.Error(err))
		}
	}
}

func (a *CachedArticleRepository) GetById(ctx context.Context, uid int64) (domain.Article, error) {
	data, err:=a.dao.GetById(ctx, uid)
	if err!=nil {
		return domain.Article{}, err
	}
	return a.toDomain(data), nil
}

func (a *CachedArticleRepository) GetPublishedById(ctx context.Context, id int64) (domain.Article, error) {
	// 如果content被放到了OSS上，就要让前端通过CDN去读content
	art, err:=a.dao.GetPubById(ctx, id)
	if err!=nil{
		return domain.Article{} ,err
	}
	// 在这边组装user，适合单体应用
	user, err:= a.userRepo.FindById(ctx, art.AuthorId)
	res := domain.Article{
		Id: art.Id,
		Title: art.Title,
		Status: domain.ArticleStatus(art.Status),
		Content: art.Content,
		// 作者信息
		Author: domain.Author{
			Id: user.Id,
			Name: user.Nickname,
		},
		CreateTime: time.UnixMilli(art.CreateTime),
		UpdateTime: time.UnixMilli(art.UpdateTime),
	}
	return res, nil
}

func (a *CachedArticleRepository) toEntity(article domain.Article) dao.Article {
	return dao.Article{
		Id:       article.Id,
		Title:    article.Title,
		Content:  article.Content,
		AuthorId: article.Author.Id,
		Status: article.Status.ToUint8(),
		CreateTime: article.CreateTime.UnixMilli(),
		UpdateTime: article.UpdateTime.UnixMilli(),
	}
}

func (a *CachedArticleRepository) toDomain(article dao.Article) domain.Article {
	return domain.Article{
		Id:       article.Id,
		Title:    article.Title,
		Content:  article.Content,
		Author: domain.Author{
			Id: article.AuthorId,
		},
		Status: domain.ArticleStatus(article.Status),
		CreateTime: time.UnixMilli(article.CreateTime),
		UpdateTime: time.UnixMilli(article.UpdateTime),
	}
}


